package org.evil.dog.gateway.filter;

import brave.Tracer;
import lombok.Data;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.AbstractServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

@Log4j2
@Component
public class LogGatewayFilterFactory extends AbstractGatewayFilterFactory<LogGatewayFilterFactory.Config> {
    private final String IP = "app-client-ip";
    private final String X_FORWARDED_FOR = "X-Forwarded-For";
    //系统换行符，用于日志打印
    private final String LINE_SEPARATOR = System.getProperty("line.separator");
    //日志记录
    private final String LOG_BUFFER = "dog_gateway_filter_log_buffer";
    @Autowired
    private LoadBalancerClient loadBalancer;

    @Autowired
    private Tracer tracer;

    public LogGatewayFilterFactory() {
        super(Config.class);
        log.info("加载日志过滤器");
    }

    @Override
    public GatewayFilter apply(Config config) {
        return new LogGateWayFilter(config, loadBalancer);
    }

    @Data
    public static class Config {
        /**
         * 不需要过滤的地址
         */
        private List<String> noFilterUrl = new ArrayList<>();
    }

    public class LogGateWayFilter extends DogGatewayFilter implements GatewayFilter, Ordered {
        private Config config;

        public LogGateWayFilter(Config config, LoadBalancerClient loadBalancer) {
            this.config = config;
            this.loadBalancer = loadBalancer;
        }

        private Mono<Void> doRequestBody(ServerWebExchange exchange, GatewayFilterChain chain) {
            Long start = System.currentTimeMillis();
            return DataBufferUtils.join(exchange.getRequest().getBody()).flatMap(dataBuffer -> {
                AtomicReference<String> bodyRef = new AtomicReference<>();
                DataBufferUtils.retain(dataBuffer);
                Flux<DataBuffer> cachedFlux = Flux.defer(() -> Flux.just(dataBuffer.slice(0, dataBuffer.readableByteCount())));
                String appClientIP = exchange.getRequest().getHeaders().getFirst(IP);
                if (appClientIP == null) {
                    appClientIP = exchange.getRequest().getRemoteAddress().getHostString();
                }
                ServerHttpRequest request = exchange.getRequest().mutate().header(IP, appClientIP).build();

                ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(request) {
                    @Override
                    public Flux<DataBuffer> getBody() {
                        return cachedFlux;
                    }
                };
                ServerWebExchange ex = exchange.mutate().request(mutatedRequest).build();
                cachedFlux.subscribe(buffer -> {
                    CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
                    DataBufferUtils.release(buffer);
                    bodyRef.set(charBuffer.toString());
                });
                StringBuilder logBuff = exchange.getAttribute(LOG_BUFFER);
                logBuff.append("^^^^^^^^^^^^requestBody^^^^^^^^^^^^").append(LINE_SEPARATOR);
                logBuff.append(bodyRef.get()).append(LINE_SEPARATOR);
                return chain.filter(ex).then(Mono.fromRunnable(() -> {
                    ServerHttpResponse response = exchange.getResponse();
                    Integer state = ((AbstractServerHttpResponse) response).getStatusCodeValue();
                    Long end = System.currentTimeMillis();
                    logBuff.append(LINE_SEPARATOR);
                    logBuff.append("===============返回信息===================").append(LINE_SEPARATOR);
                    logBuff.append("返回状态:" + state).append(LINE_SEPARATOR);
                    logBuff.append("执行时间:" + (end - start) + "ms").append(LINE_SEPARATOR);
                    logBuff.append("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@").append(LINE_SEPARATOR);
                    log.info(logBuff.toString());
                }));
            });

        }

        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            String apiPath = exchange.getRequest().getURI().getPath();
            if (config.noFilterUrl != null && !config.noFilterUrl.isEmpty() && config.noFilterUrl.contains(apiPath)) {
                return chain.filter(exchange);
            }

            //日志记录
            StringBuilder logBuffer = new StringBuilder(LINE_SEPARATOR);
            exchange.getAttributes().put(LOG_BUFFER, logBuffer);
            MediaType mediaType = exchange.getRequest().getHeaders().getContentType();
            //打印请求日志
            //获取请求信息(封装)
            ServerHttpRequest request = this.getRequestInfo(exchange);
            String tempTraceId = null;
            if (tracer != null && tracer.currentSpan() != null && tracer.currentSpan().context() != null) {
                tempTraceId = tracer.currentSpan().context().traceIdString();
            }
            String traceId = tempTraceId;
            //只对json的请求进行处理

            Long start = System.currentTimeMillis();
            return chain.filter(exchange.mutate().request(request).build()).then(Mono.fromRunnable(() -> {
                ServerHttpResponse response = exchange.getResponse();
                Integer state = ((AbstractServerHttpResponse) response).getStatusCodeValue();
                StringBuilder logBuff = exchange.getAttribute(LOG_BUFFER);
                Long end = System.currentTimeMillis();
                logBuff.append(LINE_SEPARATOR);
                logBuff.append("===============返回信息===================").append(LINE_SEPARATOR);
                logBuff.append("返回状态:" + state).append(LINE_SEPARATOR);
                logBuff.append("执行时间:" + (end - start) + "ms").append(LINE_SEPARATOR);
                logBuff.append("日志跟踪TRACE_ID:" + traceId).append(LINE_SEPARATOR);
                logBuff.append("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@").append(LINE_SEPARATOR);
                log.info(logBuff.toString());
            }));

        }

        @Override
        public int getOrder() {
            return 99;
        }

        private ServerHttpRequest getRequestInfo(ServerWebExchange exchange) {
            //增加head中客户端IP信息
            //客户端IP,由Nginx设置了head
            String appClientIP = exchange.getRequest().getHeaders().getFirst(IP);
            String ipChains = exchange.getRequest().getHeaders().getFirst(X_FORWARDED_FOR);
            if (appClientIP == null) {
                appClientIP = exchange.getRequest().getRemoteAddress().getHostString();
            }
            //获取真实地址
            String realUrl = this.getRealURL(exchange);
            //api接口地址
            String apiPath = exchange.getRequest().getURI().getPath();
            String method = exchange.getRequest().getMethodValue();
            HttpHeaders headers = exchange.getRequest().getHeaders();
            MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();
            String clientIP = appClientIP;
            ServerHttpRequest request = exchange.getRequest().mutate().header(IP, appClientIP).build();
            /////记录日志
            StringBuilder logBuffer = exchange.getAttribute(LOG_BUFFER);
            logBuffer.append("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@").append(LINE_SEPARATOR);
            logBuffer.append("------------请求信息---------------").append(LINE_SEPARATOR);
            logBuffer.append("接口名称:" + apiPath).append(LINE_SEPARATOR);
            logBuffer.append("终端IP:" + clientIP).append(LINE_SEPARATOR);
            logBuffer.append("节点中转:" + ipChains).append(LINE_SEPARATOR);
            logBuffer.append("请求地址:" + realUrl).append(LINE_SEPARATOR);
            logBuffer.append("请求方式:" + method).append(LINE_SEPARATOR);
            logBuffer.append("------------请求参数---------------").append(LINE_SEPARATOR);
            logBuffer.append("^^^^^^^^^^^^headers^^^^^^^^^^^^").append(LINE_SEPARATOR);
            headers.forEach((name, values) -> {
                values.forEach(value -> {
                    if (!"x-access-token".equals(name)) {
                        logBuffer.append(name).append("=").append(value).append(LINE_SEPARATOR);
                    }
                });
            });
            logBuffer.append("^^^^^^^^^^^^queryParams^^^^^^^^^^^^").append(LINE_SEPARATOR);
            queryParams.forEach((name, value) -> {
                logBuffer.append(name).append("=").append(value).append(LINE_SEPARATOR);
            });
            return request;
        }
    }


}
